package com.gitee.ixtf.vertx.internal.converter

import com.gitee.ixtf.core.J
import com.gitee.ixtf.core.MAPPER
import com.gitee.ixtf.vertx.ParameterConverter
import graphql.schema.DataFetchingEnvironment
import io.vertx.core.buffer.Buffer
import io.vertx.core.eventbus.Message
import io.vertx.core.json.JsonArray
import io.vertx.ext.web.FileUpload
import io.vertx.ext.web.RoutingContext
import java.lang.reflect.Parameter
import java.lang.reflect.ParameterizedType
import java.lang.reflect.WildcardType

class Body2IterableParameterConverter(private val parameter: Parameter) : ParameterConverter<Any?> {
  private val elementClass by lazy {
    val parameterizedType = parameter.parameterizedType as ParameterizedType
    val type = parameterizedType.actualTypeArguments[0]
    val elementType = if (type is WildcardType) type.upperBounds[0] else type
    elementType as Class<*>
  }
  private val fileUploadElement by lazy { FileUpload::class.java.isAssignableFrom(elementClass) }
  private val collectionLikeType by lazy {
    MAPPER.typeFactory.constructCollectionLikeType(parameter.type, elementClass)
  }

  private fun convertBody(body: Any?): Any? =
      when (body) {
        is String -> MAPPER.readValue(body, collectionLikeType)
        is ByteArray -> MAPPER.readValue(body, collectionLikeType)
        is Buffer -> MAPPER.readValue(body.bytes, collectionLikeType)
        is JsonArray -> MAPPER.readValue(body.toBuffer().bytes, collectionLikeType)
        else -> body
      }

  override suspend fun convert(message: Message<*>) =
      if (fileUploadElement) FileUploadsParameterConverter.convert(message)
      else message.body()?.let { convertBody(it) }

  override suspend fun convert(rc: RoutingContext) =
      if (fileUploadElement) FileUploadsParameterConverter.convert(rc)
      else rc.body()?.takeUnless { it.isEmpty }?.run { convertBody(buffer()) }

  override suspend fun convert(env: DataFetchingEnvironment): Any? =
      if (fileUploadElement) FileUploadsParameterConverter.convert(env)
      else J.inputCommand(MAPPER.convertValue(env.arguments, parameter.type))
}
